Skip to content

feat(rewrite): introspect the condition of a ternary - #14816

Draft
RonnyPfannschmidt wants to merge 5 commits into
pytest-dev:mainfrom
RonnyPfannschmidt:ronny/rewrite-visit-ifexp
Draft

feat(rewrite): introspect the condition of a ternary#14816
RonnyPfannschmidt wants to merge 5 commits into
pytest-dev:mainfrom
RonnyPfannschmidt:ronny/rewrite-visit-ifexp

Conversation

@RonnyPfannschmidt

@RonnyPfannschmidt RonnyPfannschmidt commented Jul 31, 2026

Copy link
Copy Markdown
Member

Stacked on #14815#14814#14447#14921#14813. Its diff includes theirs.

A conditional expression showed only its result, so a failure gave no hint which way it went.

assert 0 == 99
 +  where 0 = (... if True else ...)

The branches keep their original nodes: only the selected one may run, so neither can be hoisted into a statement. That is also why this visitor needs no freeze, unlike the subscript container in #14815 — leaving the branches in place leaves them evaluated after the condition, which is Python's order. TestEvaluationOrder::test_ifexp_branches_in_order in #14813 covers that claim rather than leaving it as a comment.

Lands the introspect-ifexp cases of the coverage matrix in #14813, as passing tests (+3).

Supersedes part of #14448.

RonnyPfannschmidt and others added 5 commits September 1, 2026 13:51
visit_operand() only froze a bare name, so two other unhoisted operands
kept being evaluated after everything that follows them:

    assert collect((x := 1), identity(x := 2)) == (1, 2)
    assert collect(*items, identity(items := [9])) == (1, [9])

A walrus operator left in place assigns once the enclosing expression is
assembled, which is after the later arguments have run -- so the earlier
argument saw the later assignment.  A starred argument hid its value
inside an ast.Starred, where the existing Name check could not see it.

Closes the order-starred-argument group and the remaining
order-call-argument entry in the coverage matrix.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…eeds

visit_operand freezes a walrus operand whenever anything follows it, and
a comparison always has at least one comparator -- so by the time
visit_Compare looks at its left operand, a NamedExpr has already been
copied into a temporary.  The special case that did it here can never
run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The rewriter only ever visits expressions inside an assert condition, so
an attribute always arrives in Load context and the fallback never runs.
Removing it keeps the next visitor from copying a guard that cannot fire.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A subscript was opaque: the message showed the value it produced with no
indication of which container or key it came from.  Decompose it the way
attribute access already is.

The container goes through visit_operand() because taking the expression
away from generic_visit() takes away the hoisting that kept it ordered --
without that, `assert box[identity(box := other)] == 1` would start
reading the post-walrus container.  The order-axis guard in the coverage
matrix fails if this is dropped.

Slices keep the generic treatment; decomposing start/stop/step is rarely
what a failure message needs.

Closes the introspect-subscript group in the coverage matrix.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A conditional expression showed only its result, so a failure gave no
hint which way it went.  Introspect the condition and report it as
"(... if <cond> else ...)".

The branches keep their original nodes: only the selected one may run,
so neither can be hoisted into a statement.  That leaves them evaluated
after the condition, which is Python's order, so unlike the subscript
container they need no freeze -- the order-axis guard covers it.

Closes the introspect-ifexp group in the coverage matrix.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@RonnyPfannschmidt
RonnyPfannschmidt force-pushed the ronny/rewrite-visit-ifexp branch from c812cee to 7b3faf5 Compare September 1, 2026 11:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:chronographer:provided (automation) changelog entry is part of PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant